/*
 * Description: 命名管道帮助类
 */
#include "named_pipe_util.h"
#include "proc_util.h"
#include "taskbar_monitor.h"

namespace {
    constexpr auto BUFF_SIZE = 1024;
    constexpr auto PIPE_NAME = R"(\\.\PIPE\WIN_EVENT)";
    constexpr auto PIPE_CONN_WAIT_DURATION = 20000;
}

NamedPipeUtil& NamedPipeUtil::GetInstance()
{
    static NamedPipeUtil instance;
    return instance;
}

bool NamedPipeUtil::Init()
{
    LOG_INFO("NamedPipeUtil Init");
    stopFlag = false;
    bool ret = OpenPipe();
    if (!ret) {
        LOG_ERR("OpenPipe failed");
        return ret;
    }

    DWORD dwMode = PIPE_READMODE_MESSAGE;
    if (!SetNamedPipeHandleState(pipeHandle, &dwMode, nullptr, nullptr)) {
        LOG_ERR("SetNamedPipeHandleState failed, err: {}", GetLastError());
        return false;
    }
    return true;
}

void NamedPipeUtil::DeInit()
{
    stopFlag = true;
    CancelIoEx(pipeHandle, nullptr);
    SAFE_CLOSE_HANDLE(pipeHandle, INVALID_HANDLE_VALUE);
}

void NamedPipeUtil::Send(const std::string& msg)
{
    std::lock_guard<std::mutex> lk(sendMtx);
    if (pipeHandle == INVALID_HANDLE_VALUE) {
        LOG_ERR("Invalid pipe handle");
        return;
    }
    LPCTSTR lpMessage = msg.c_str();
    DWORD dwBytes = (lstrlen(lpMessage) + 1) * sizeof TCHAR;
    DWORD dwBytesWritten = 0;
    OVERLAPPED overLapped;
    overLapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
    if (!overLapped.hEvent) {
        LOG_ERR("Failed to create event for overlap while send");
        return;
    }
    overLapped.Offset = 0;
    overLapped.OffsetHigh = 0;
    BOOL ret = WriteFile(pipeHandle, lpMessage, dwBytes, &dwBytesWritten, &overLapped);
    if (!ret) {
        if (GetLastError() != ERROR_IO_PENDING) {
            LOG_ERR("WriteFile failed, err: {}", GetLastError());
            SAFE_CLOSE_HANDLE(overLapped.hEvent, INVALID_HANDLE_VALUE);
            return;
        }
        ret = GetOverlappedResult(pipeHandle, &overLapped, &dwBytesWritten, TRUE);
    }
    if (ret) {
        LOG_INFO("Send success");
    } else {
        LOG_ERR("Send failed, err: {}", GetLastError());
    }
    SAFE_CLOSE_HANDLE(overLapped.hEvent, INVALID_HANDLE_VALUE);
}

void NamedPipeUtil::Recv()
{
    while (!stopFlag) {
        TCHAR* pchRequest = static_cast<TCHAR*>(malloc(BUFF_SIZE * sizeof TCHAR));
        if (pchRequest == nullptr) {
            LOG_ERR("pchRequest malloc failed!");
            break;
        }
        DWORD dwBytesRead = 0;
        OVERLAPPED overLapped;
        overLapped.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
        if (!overLapped.hEvent) {
            free(pchRequest);
            LOG_ERR("Failed to create event for overlap while recv");
            break;
        }
        overLapped.Offset = 0;
        overLapped.OffsetHigh = 0;
        BOOL ret = ReadFile(pipeHandle, pchRequest, BUFF_SIZE * sizeof TCHAR, &dwBytesRead, nullptr);
        if (!ret) {
            if (GetLastError() == ERROR_IO_PENDING) {
                ret = GetOverlappedResult(pipeHandle, &overLapped, &dwBytesRead, TRUE);
            }
        }
        if (!ret || dwBytesRead == 0) {
            LOG_ERR("ReadFile Failed, errCode: {}", GetLastError());
            free(pchRequest);
            SAFE_CLOSE_HANDLE(overLapped.hEvent, INVALID_HANDLE_VALUE);
            break;
        }
        std::string request(pchRequest);
        free(pchRequest);
        SAFE_CLOSE_HANDLE(overLapped.hEvent, INVALID_HANDLE_VALUE);
        LOG_INFO("Recv msg from agent_service: {}", request.c_str());
    }
    LOG_INFO("Recv finished");
}

bool NamedPipeUtil::OpenPipe()
{
    if (!WaitNamedPipe(PIPE_NAME, PIPE_CONN_WAIT_DURATION)) {
        LOG_ERR("WaitNamedPipe failed, err: {}", GetLastError());
        return false;
    }
    pipeHandle = CreateFile(PIPE_NAME, GENERIC_READ | GENERIC_WRITE,
        0, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr);
    if (pipeHandle == INVALID_HANDLE_VALUE) {
        LOG_ERR("CreateFile failed, err: {}", GetLastError());
        return false;
    }
    LOG_INFO("OpenPipe success");
    return true;
}
